package com.atguigu.gmall.search.service.impl;
import com.atguigu.gmall.search.entity.SearchAttr;
import com.google.common.collect.Lists;
import com.atguigu.gmall.search.vo.SearchRespVo.SortVo;

import com.atguigu.gmall.search.entity.Goods;
import com.atguigu.gmall.search.service.GoodsSearchService;
import com.atguigu.gmall.search.vo.SearchParamVo;
import com.atguigu.gmall.search.vo.SearchRespVo;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.index.query.*;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.nested.NestedAggregationBuilder;
import org.elasticsearch.search.aggregations.bucket.nested.ParsedNested;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedLongTerms;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.SearchHit;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.document.Document;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.query.HighlightQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.Query;
import org.springframework.data.elasticsearch.core.query.UpdateQuery;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author lfy
 * @Description
 * @create 2023-07-24 13:57
 */
@Service
public class GoodsSearchServiceImpl implements GoodsSearchService {


    @Autowired
    ElasticsearchRestTemplate esTemplate;


    @Override
    public SearchRespVo search(SearchParamVo param) {

        //1、根据请求带来的参数，构建DSL查询条件
        Query query = buildDSL(param);

        SearchHits<Goods> searchHits = esTemplate.search(query, Goods.class, IndexCoordinates.of("goods"));

        //2、把检索的结果封装成 SearchRespVo;
        SearchRespVo vo = buildRespVo(searchHits,param);

        //把结果转成respvo
        return vo;
    }

    @Override
    public void updateSkuHotScore(Long skuId, Long score) {

        Document document = Document.create();
        document.append("hotScore",score);

        UpdateQuery query = UpdateQuery.builder(skuId.toString())
                .withDocAsUpsert(true) //增量更新
                .withDocument(document)
                .build();
        esTemplate.update(query,IndexCoordinates.of("goods"));
    }


    /**
     * 根据检索结果，构建vo响应
     * @param searchHits
     * @return
     */
    private SearchRespVo buildRespVo(SearchHits<Goods> searchHits,SearchParamVo param) {
        SearchRespVo resp = new SearchRespVo();

        //1、查询参数
        resp.setSearchParam(param);
        //2、商品列表
        List<SearchHit<Goods>> hits = searchHits.getSearchHits();
        List<Goods> goods = hits.stream()
                .map(item -> {
                    Goods content = item.getContent();
                    //如果我们当前用了模糊检索，需要用高亮后的title替换原来的title
                    if (StringUtils.hasText(param.getKeyword())) {
                        //替换title
                        String title = item.getHighlightField("title").get(0);
                        content.setTitle(title);
                    }
                    return content;
                })
                .collect(Collectors.toList());
        resp.setGoodsList(goods);

        //3、品牌面包屑
        if(StringUtils.hasText(param.getTrademark())){
            String trademark = param.getTrademark(); //trademark=1:小米
            resp.setTrademarkParam("品牌："+trademark.split(":")[1]);
        }




        //4、属性面包屑
        if(param.getProps()!=null && param.getProps().length > 0){
            List<SearchAttr> attrs = Arrays.stream(param.getProps())
                    .map(item -> {
                        SearchAttr attr = new SearchAttr();
                        String[] split = item.split(":");
                        attr.setAttrId(Long.parseLong(split[0]));
                        attr.setAttrValue(split[1]);
                        attr.setAttrName(split[2]);
                        return attr;
                    }).collect(Collectors.toList());
            resp.setPropsParamList(attrs);
        }


        //5、品牌列表检索区
        //获取到所有的聚合结果
        Aggregations aggregations = searchHits.getAggregations();
        //拿到指定名字的聚合结果  Parsed_Xxx(聚合用的属性的类型)_Xxx(聚合的类型)
        ParsedLongTerms tmIdAgg = aggregations.get("tmIdAgg");

        List<SearchRespVo.TrademarkVo> trademarkVos = tmIdAgg.getBuckets().stream()
                .map(item -> {
                    SearchRespVo.TrademarkVo trademarkVo = new SearchRespVo.TrademarkVo();
                    //品牌id
                    long tmId = item.getKeyAsNumber().longValue();
                    trademarkVo.setTmId(tmId);

                    //品牌名
                    ParsedStringTerms tmNameAgg = item.getAggregations().get("tmNameAgg");
                    String tmName = tmNameAgg.getBuckets().get(0).getKeyAsString();
                    trademarkVo.setTmName(tmName);

                    //品牌logo
                    ParsedStringTerms tmLogoAgg = item.getAggregations().get("tmLogoAgg");
                    String tmLogo = tmLogoAgg.getBuckets().get(0).getKeyAsString();
                    trademarkVo.setTmLogoUrl(tmLogo);

                    return trademarkVo;
                }).collect(Collectors.toList());
        resp.setTrademarkList(trademarkVos);

        //6、平台属性检索区
        ParsedNested attrsAgg = searchHits.getAggregations().get("attrsAgg");
        ParsedLongTerms attrIdAgg = attrsAgg.getAggregations().get("attrIdAgg");

        List<SearchRespVo.AttrVo> attrVos = attrIdAgg.getBuckets().stream()
                .map(item -> {
                    SearchRespVo.AttrVo attrVo = new SearchRespVo.AttrVo();
                    //1、属性id
                    long attrId = item.getKeyAsNumber().longValue();
                    attrVo.setAttrId(attrId);

                    ParsedStringTerms attrNameAgg = item.getAggregations().get("attrNameAgg");
                    //2、属性 name
                    String attrName = attrNameAgg.getBuckets().get(0).getKeyAsString();
                    attrVo.setAttrName(attrName);

                    //3、属性 value
                    ParsedStringTerms attrValueAgg = item.getAggregations().get("attrValueAgg");

                    List<String> valueList = attrValueAgg.getBuckets().stream()
                            .map(bucket -> bucket.getKeyAsString())
                            .collect(Collectors.toList());
                    attrVo.setAttrValueList(valueList);

                    return attrVo;
                }).collect(Collectors.toList());

        resp.setAttrsList(attrVos);


        //7、排序高亮回显
        String order = param.getOrder();
        SortVo sortVo = new SortVo();
        String[] split = order.split(":");
        sortVo.setType(split[0]);
        sortVo.setSort(split[1]);
        resp.setOrderMap(sortVo);


        //8、页码
        resp.setPageNo(param.getPageNo());
        //9、总页码   totalHits%pageSize==0?totalHits/pageSize:totalHits/pageSize + 1
        long totalHits = searchHits.getTotalHits();
        double d = new Double(totalHits)/new Double(param.getPageSize());
        resp.setTotalPages((long) Math.ceil(d));

        //10、urlparam； 原来的整个url路径及后面的查询参数； 记录当前url的所有参数
        String urlparam = buildUrlParam(param);
        resp.setUrlParam(urlparam);

        return resp;
    }

    private String buildUrlParam(SearchParamVo param) {
        //复原前端当时发请求的完整url已经参数
        StringBuilder sb = new StringBuilder("list.html?");
        if(param.getCategory1Id()!=null){
            sb.append("&category1Id="+param.getCategory1Id());
        }
        if(param.getCategory2Id()!=null){
            sb.append("&category2Id="+param.getCategory2Id());
        }
        if(param.getCategory3Id()!=null){
            sb.append("&category3Id="+param.getCategory3Id());
        }

        if (StringUtils.hasText(param.getKeyword())) {
            sb.append("&keyword="+param.getKeyword());
        }

//        if (StringUtils.hasText(param.getOrder())) {
//            sb.append("&order="+param.getOrder());
//        }

        if (StringUtils.hasText(param.getTrademark())) {
            sb.append("&trademark="+param.getTrademark());
        }

//        if(param.getPageNo()!=null){
//            sb.append("&pageNo="+param.getPageNo());
//        }

        if(param.getProps()!=null && param.getProps().length > 0){
            Arrays.stream(param.getProps()).forEach(prop->{
                sb.append("&props="+prop);
            });
        }

        return sb.toString();
    }


    /**
     * 根据请求带来的参数，构建DSL查询条件
     * @param param
     * @return
     */
    private Query buildDSL(SearchParamVo param) {
        //1、准备一个 queryBuilder
        BoolQueryBuilder bool = new BoolQueryBuilder();

        //2、根据不同 param 传入的条件，构建不同的Must
        //=========三级分类查询条件============
        if(param.getCategory1Id()!=null){
            bool.must(new TermQueryBuilder("category1Id",param.getCategory1Id()));
        }

        if(param.getCategory2Id()!=null){
            bool.must(new TermQueryBuilder("category2Id",param.getCategory2Id()));
        }

        if(param.getCategory3Id()!=null){
            bool.must(new TermQueryBuilder("category3Id",param.getCategory3Id()));
        }

        //========全文检索查询条件==========
        if(StringUtils.hasText(param.getKeyword())){
            bool.must(new MatchQueryBuilder("title",param.getKeyword()));
        }

        //========品牌检索条件=============
        if(StringUtils.hasText(param.getTrademark())){
            bool.must(new TermQueryBuilder("tmId",param.getTrademark().split(":")[0]));
        }

        //========平台属性检索条件===========
        if(param.getProps()!=null && param.getProps().length>0){
            Arrays.stream(param.getProps())
                    .forEach(prop->{
                        BoolQueryBuilder nestedBool = new BoolQueryBuilder();

                        String[] split = prop.split(":");
                        //按照属性id
                        nestedBool.must(new TermQueryBuilder("attrs.attrId",split[0]));
                        nestedBool.must(new TermQueryBuilder("attrs.attrValue",split[1]));

                        NestedQueryBuilder nested = new NestedQueryBuilder("attrs",nestedBool, ScoreMode.None);
                        bool.must(nested);
                    });
        }


        //2、根据 queryBuilder 构造出 Query 对象
        NativeSearchQuery dsl = new NativeSearchQuery(bool);

        //=======排序条件===============
        if(StringUtils.hasText(param.getOrder())){
            String[] split = param.getOrder().split(":");
            String field = "";
            switch (split[0]){
                case "1": field = "hotScore"; break;
                case "2": field = "price"; break;
                default: field = "hotScore";
            }
            Sort sort = Sort.by(Sort.Direction.fromString(split[1]), field);
            dsl.addSort(sort);
        }

        //======分页条件==========
        PageRequest request = PageRequest.of(param.getPageNo()-1, param.getPageSize());
        dsl.setPageable(request);

        //=====高亮条件========
        if (StringUtils.hasText(param.getKeyword())) {
            HighlightBuilder builder = new HighlightBuilder();
            builder.field("title")
                    .preTags("<span style='color:red'>")
                    .postTags("</span>");

            HighlightQuery highlightQuery = new HighlightQuery(builder);
            dsl.setHighlightQuery(highlightQuery);
        }



        //======品牌聚合条件==========
        //1、按照品牌id聚合
        TermsAggregationBuilder tmIdAgg = AggregationBuilders
                .terms("tmIdAgg")
                .field("tmId").size(100);


        //2、基于品牌id - 按照品牌名进行一次子聚合
        tmIdAgg.subAggregation(AggregationBuilders.terms("tmNameAgg")
                .field("tmName").size(1));

        //3、基于品牌id - 按照品牌logo进行一次子聚合
        tmIdAgg.subAggregation(AggregationBuilders.terms("tmLogoAgg")
                .field("tmLogoUrl").size(1));

        dsl.addAggregation(tmIdAgg);

        //=======属性聚合条件========
        //1、构造嵌入式聚合
        NestedAggregationBuilder attrsAggNested = AggregationBuilders.nested("attrsAgg", "attrs");


        //2、构造属性id子聚合
        TermsAggregationBuilder attrIdAgg = AggregationBuilders.terms("attrIdAgg")
                .field("attrs.attrId").size(100);

        //3、构造属性 name、孙子聚合
        TermsAggregationBuilder attrNameAgg = AggregationBuilders.terms("attrNameAgg")
                .field("attrs.attrName").size(1);
        attrIdAgg.subAggregation(attrNameAgg);
        //4、构造属性 value、孙子聚合
        TermsAggregationBuilder attrValueAgg = AggregationBuilders.terms("attrValueAgg")
                .field("attrs.attrValue").size(100);
        attrIdAgg.subAggregation(attrValueAgg);

        attrsAggNested.subAggregation(attrIdAgg);

        dsl.addAggregation(attrsAggNested);

        return dsl;
    }
}
